package cn.dansj.common.utils.redis.lock;

import cn.dansj.common.processor.meta.WriteToAutoConfiguration;
import cn.dansj.common.utils.lock.Lock;
import cn.dansj.common.utils.lock.LockException;
import cn.dansj.common.utils.lock.LockFactory;
import cn.dansj.common.utils.spring.AnnotationUtils;
import cn.dansj.common.utils.spring.advisor.AbstractBeanMethodFactoryPointcutAdvisor;
import cn.dansj.common.utils.spring.context.ApplicationContextProvider;
import cn.dansj.common.utils.spring.context.spel.KeyBuilder;
import cn.dansj.common.utils.spring.request.RequestUtils;
import cn.dansj.common.utils.transfer.StringUtils;
import cn.dansj.common.utils.transfer.Transformation;
import cn.dansj.common.utils.transfer.Verification;
import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

@WriteToAutoConfiguration
@Order(3)
public class LockAnnotationAdvisor extends AbstractBeanMethodFactoryPointcutAdvisor {
    private static final Logger logger = LoggerFactory.getLogger(LockAnnotationAdvisor.class);

    @Override
    public boolean match(Method method, Class<?> targetClass) {
        return AnnotationUtils.hasAnnotation(method, Lock4j.class);
    }

    @Override
    public Object execute(MethodInvocation invocation) throws Throwable {
        Lock4j lock4j = AnnotationUtils.findMergedAnnotation(invocation.getMethod(), Lock4j.class);
        String prefix = Transformation.nvl(lock4j.prefix(), "lock");
        String lock = Verification.checkNotNull(lock4j.name()) ? lock4j.name() : invocation.getMethod().getDeclaringClass().getName() + "." + invocation.getMethod().getName();
        String key = prefix + "@" + ApplicationContextProvider.getBean(KeyBuilder.class).buildKey(invocation, new String[]{lock});

        long lockExpireTimeMs = lock4j.lockExpireTimeMs();
        long lockWaitMaxMs = lock4j.tryGetLockWaitMaxMs();
        String returnMsg = lock4j.returnMsg();

        try (Lock ignored = LockFactory.createLock(lock4j.lockType(), key, lockWaitMaxMs, lockExpireTimeMs, true, null)) {
            return invocation.proceed();
        } catch (LockException lockException) {
            String errorMessage = Verification.checkNotNull(returnMsg) ? returnMsg : lockException.getMessage();
            Method invocationMethod = invocation.getMethod();
            Parameter[] parameters = invocationMethod.getParameters();
            String methodInfo = invocationMethod.getDeclaringClass().getName() + "." + invocationMethod.getName() + (parameters.length > 0 ? "(" + StringUtils.join(parameters, ", ") + ")" : "");
            logger.error("@Lock4j on {} 注解失败: {}", methodInfo, lockException.getMessage());

            if (RequestUtils.checkInWebRequest() && lock4j.returnErrorInWebRequest()) {
                return RequestUtils.response(500, errorMessage);
            }
            throw new LockException(errorMessage);
        }
    }
}

